package com.antgroup.seckill.business.goods.controller;

import com.alibaba.fastjson.JSON;
import com.antgroup.seckill.business.common.enums.RespBeanEnum;
import com.antgroup.seckill.business.common.exception.GlobalException;
import com.antgroup.seckill.business.common.rabbitmq.MQSender;
import com.antgroup.seckill.business.common.config.AccessLimit;
import com.antgroup.seckill.business.common.rocketmq.RocketMqSender;
import com.antgroup.seckill.business.common.vo.response.GoodsVo;
import com.antgroup.seckill.business.common.vo.response.RespBean;
import com.antgroup.seckill.business.common.vo.response.SeckillOrderDetailVo;
import com.antgroup.seckill.business.goods.model.Order;
import com.antgroup.seckill.business.goods.model.SeckillMessage;
import com.antgroup.seckill.business.goods.model.SeckillOrder;
import com.antgroup.seckill.business.goods.model.User;
import com.antgroup.seckill.business.goods.service.IGoodsService;
import com.antgroup.seckill.business.goods.service.IOrderService;
import com.antgroup.seckill.business.goods.service.ISeckillGoodsService;
import com.antgroup.seckill.business.goods.service.ISeckillOrderService;
import com.wf.captcha.ArithmeticCaptcha;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.antgroup.seckill.business.common.enums.RespBeanEnum.NOT_LOGIN;

/**
 * @author wangxy
 * @date 2024/7/31 22:31
 */
@Controller
@RequestMapping("/seckill")
@Slf4j
public class SeckillController implements InitializingBean {

    @Autowired
    private ISeckillOrderService iSeckillOrderService;


    @Autowired
    private ISeckillGoodsService iSeckillGoodsService;


    @Autowired
    private IOrderService iOrderService;


    @Autowired
    private IGoodsService iGoodsService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MQSender mqSender;

    @Autowired
    private RocketMqSender rocketMqSender;





    //内存标记（key为商品ID，value为是否还存在）
    private Map<String,Boolean> map = new HashMap<>();


    /**
     * 秒杀用模版引擎，弃用
     *
     * @param model
     * @param user
     * @param goodsId
     * @return
     */

    @GetMapping("/doSeckill")
    public String doSeckill(Model model, User user, @RequestParam(required = true) String goodsId) {
        if (null == user) {
            return "login";
        }

        //判断库存还有没有
        //SeckillGoods seckillGoods = iSeckillGoodsService.querySeckillGoodsByGoodsId(goodsId);

        model.addAttribute("user", user);
        GoodsVo goods = iGoodsService.queryGoodsByGoodsId(goodsId);
        if (goods.getSeckillCount() < 1) {
            model.addAttribute("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage());
            return "seckillFail";
        }


        //判断用户是否已经秒杀过
        SeckillOrder seckillOrder = iSeckillOrderService.querySeckillOrderByUserId(user.getId());
        if (Objects.nonNull(seckillOrder)) {
            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
            return "seckillFail";
        }

        //秒杀商品
        Order order = iOrderService.seckillGoods(goods, user);
        model.addAttribute("order", order);
        model.addAttribute("goods", goods);


        return "seckillPlay";
    }


    /**
     * 秒杀第二版（查DB还是效率不高，弃用）
     *
     * @param model
     * @param user
     * @param goodsId
     * @return
     */
    @GetMapping("/doStaticSeckill")
    @ResponseBody
    public RespBean doStaticSeckill(Model model, User user, @RequestParam(required = true) String goodsId) {
        if (Objects.isNull(user)) {
            return RespBean.error(NOT_LOGIN);
        }
        //判断库存还有没有
        //SeckillGoods seckillGoods = iSeckillGoodsService.querySeckillGoodsByGoodsId(goodsId);

        GoodsVo goods = iGoodsService.queryGoodsByGoodsId(goodsId);
        if (goods.getSeckillCount() < 1) {

            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }


        //判断用户是否已经秒杀过
        /*SeckillOrder seckillOrder = iSeckillOrderService.querySeckillOrderByUserId(user.getId());
        if(Objects.nonNull(seckillOrder)) {
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }
*/
        //从redis中获取
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("seckill:" + user.getId() + ":" + goods.getId());
        if (Objects.nonNull(seckillOrder)) {
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }

        //秒杀商品
        Order order = iOrderService.seckillGoods(goods, user);
        SeckillOrderDetailVo seckillOrderDetailVo = new SeckillOrderDetailVo();
        seckillOrderDetailVo.setUser(user);
        seckillOrderDetailVo.setOrder(order);
        seckillOrderDetailVo.setGoods(goods);
        return RespBean.success(seckillOrderDetailVo);

    }

    /**
     * 秒杀第三版 商品库存初始化化到redis 预检库存，最终版本
     * 用户在点击秒杀的时候，先去后端生成一个真实的秒杀地址，然后用户点击这个地址把这个唯一的ID带到下面的 {path}上面，才是秒杀地址
     *
     * @param model
     * @param user
     * @param goodsId
     * @return
     */
    @GetMapping("/{path}/seckillGoods")
    @ResponseBody
    public RespBean seckillGoods(Model model, User user, @RequestParam(required = true) String goodsId,@PathVariable String path) {
        if (Objects.isNull(user)) {
            return RespBean.error(NOT_LOGIN);
        }

        //判断秒杀地址正确性
       boolean flag = iSeckillOrderService.checkSeckillPath(user,goodsId,path);
        if(!flag) {
            return RespBean.error(RespBeanEnum.ILLEGAL_LOGIN);
        }


        //判断是否重复抢购
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        if (Objects.nonNull(seckillOrder)) {
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }

        //如果redis库存不足就不用去查redis了
        if(map.get(goodsId)) {
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);

        }

        //获取redis
        ValueOperations valueOperations = redisTemplate.opsForValue();

        //也可以直接用redis的脚本实现高并发抢购减库存
        /*String script = "if tonumber(redis.call('get',KEYS[1]))>0 then redis.call('decr',KEYS[1]); redis.call('lpush',KEYS[2],ARGV[1]) return 1; else return 0; end";

        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        List<String> keyList = new ArrayList<>();
        keyList.add("goods:" + goodsId);
        keyList.add("user:" + user.getId());

        Long result = (Long) redisTemplate.execute(redisScript, keyList, user.getId());
        ValueOperations<String,Object> ops = redisTemplate.opsForValue();
        ListOperations<String,Object> listOperations = redisTemplate.opsForList();

        if(result == 1) {
            System.out.println(ops.get("goods:" + goodsId));
            System.out.println(listOperations.range("user:" + user.getId(),0,-1));
            return RespBean.success(RespBeanEnum.SECKILL_SUCCESS);
        }else {
            return RespBean.error(RespBeanEnum.SECKILL_FAIL);
        }*/

        //预检库存
        Long skillStock = valueOperations.decrement("seckillGoods:" + goodsId);

        if(skillStock < 0) {
            valueOperations.increment("seckillGoods:" + goodsId);
            map.put(goodsId,true);
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }

        //异步发消息到rabbmitMq
        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
        //mqSender.sendSeckillMessage(JSON.toJSONString(seckillMessage));

        //异步发消息到rocketMq
        rocketMqSender.sendMessage("seckill-order:CREATE", JSON.toJSONString(seckillMessage));
        return RespBean.success(0);

    }


    /**
     *  秒杀完成以后，前端页面来轮训查询是否秒杀成功
     *
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping("/querySeckillGoodsOrder")
    @ResponseBody
    public RespBean querySeckillGoodsOrder(User user,String goodsId) {
        if(Objects.isNull(user)) {
            return RespBean.error(NOT_LOGIN);
        }

        String orderId = iSeckillOrderService.querySeckillGoodsOrder(user,goodsId);
        return RespBean.success(orderId);

    }

    /**
     * 获取秒杀地址（根据每个用户ID返回一个生成一个唯一ID返回给前端，前端把这个拼接到真正的表示地址上面在校验这个地址）
     *
     * @param user
     * @param goodsId
     * @return
     */
    @AccessLimit(second = 5,maxCount = 5,needLogin = true)
    @RequestMapping("/createSeckillPath")
    @ResponseBody
    public RespBean createSeckillPath(User user, @RequestParam String goodsId, @RequestParam(required = false) String captcha,HttpServletRequest request) {
        if(Objects.isNull(user)) {
            return RespBean.error(NOT_LOGIN);
        }
        
        //校验验证码
        boolean flag = iSeckillOrderService.checkCaptch(user,goodsId,captcha);
        if(!flag) {
            return RespBean.error(RespBeanEnum.CAPTCH_ERROR);
        }

        String seckillPath = iSeckillOrderService.createSeckillPath(user,goodsId);
        return RespBean.success(seckillPath);

    }


    /**
     *  生成验证码
     *
     * @param user
     * @param goodsId
     * @param response
     */
    @RequestMapping("/captcha")
    public void captcha(User user, @RequestParam String goodsId, HttpServletResponse response) {
        if(Objects.isNull(user)) {
            throw new GlobalException(RespBeanEnum.ILLEGAL_LOGIN);
        }

        response.setContentType("image/gif");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        //生成验证码放入redis
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 32,3);
        captcha.setLen(3);  // 几位数运算，默认是两位
        captcha.getArithmeticString();  // 获取运算的公式：3+2=?
        captcha.text();  // 获取运算的结果：5

        redisTemplate.opsForValue().set("captcha:" + user.getId() + ":" + goodsId,captcha.text(),300,TimeUnit.SECONDS);

        try {
            captcha.out(response.getOutputStream());  // 输出验证码
        } catch (IOException e) {
            log.error("验证码生成异常，" + e.getMessage());
        }


    }

    /**
     * 把商品库存初始化到redis
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {

        List<GoodsVo> goodsVoList = iGoodsService.queryAllGoods();

        Optional.ofNullable(goodsVoList).orElse(new ArrayList<>()).stream().forEach(goodsVo -> {
            redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(), goodsVo.getSeckillCount(), 60, TimeUnit.MINUTES);
            map.put(goodsVo.getId(),false);
        });
    }
}
